/*
*         Copyright (c), NXP Semiconductors Bangalore / India
*
*                     (C)NXP Semiconductors
*       All rights are reserved. Reproduction in whole or in part is
*      prohibited without the written consent of the copyright owner.
*  NXP reserves the right to make changes without notice at any time.
* NXP makes no warranty, expressed, implied or statutory, including but
* not limited to any implied warranty of merchantability or fitness for any
*particular purpose, or that the use will not infringe any third party patent,
* copyright or trademark. NXP must not be liable for any loss or damage
*                          arising from its use.
*/

/** \file
* Generic phDriver Component of Reader Library Framework.
* $Author$
* $Revision$
* $Date$
*
* History:
*  RS: Generated 24. Jan 2017
*
*/


#include "phDriver.h"

#include <DAL/cfg/BoardSelection.h>
#include "FreeRTOS.h"
#include "task.h"
#include <BSP.h>
#include <IO.h>

static const uint32_t portTable[] = {
	GPIOA, GPIOB, GPIOC
};

static pphDriver_TimerCallBck_t pTimerIsrCallBack;
static volatile uint32_t dwTimerExp;

static void phDriver_TimerIsrCallBack(void);
static uint32_t timer_get_freq(void){
	uint32_t uclk;
	uclk = rcu_clock_freq_get(CK_APB2);
	return uclk / 2;
}

phStatus_t phDriver_TimerStart(phDriver_Timer_Unit_t eTimerUnit, uint32_t dwTimePeriod, pphDriver_TimerCallBck_t pTimerCallBack)
{
	uint32_t uclk;
	uint32_t period;
	timer_parameter_struct timer_initpara;

	rcu_periph_clock_enable(RCU_TIMER16);
	timer_deinit(TIMER16);
	uclk = timer_get_freq();
	uint32_t psc;
	if(eTimerUnit == PH_DRIVER_TIMER_MICRO_SECS){
		psc = uclk/1000000U - 1;
		period = dwTimePeriod;
	}
	else{
		//分辨率200us
		psc = uclk/5000U - 1;
		period = dwTimePeriod*5U;
		if(eTimerUnit == PH_DRIVER_TIMER_SECS){
			period *= 1000;
		}
	}
//		period = dwTimePeriod * (PH_DRIVER_TIMER_MICRO_SECS / eTimerUnit);
	if(period > 0xFFFF ){
        return PH_DRIVER_ERROR | PH_COMP_DRIVER;
	}
    if(pTimerCallBack == NULL)
    {    /* Timer Start is blocking call. */
        dwTimerExp = 0;
        pTimerIsrCallBack = phDriver_TimerIsrCallBack;
    }
    else
    {   /* Call the Timer callback. */
        pTimerIsrCallBack = pTimerCallBack;
    }
	
	/* TIMER16 configuration */
	timer_initpara.prescaler         = psc;
	timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
	timer_initpara.counterdirection  = TIMER_COUNTER_UP;
	timer_initpara.period            = period;
	timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
	timer_init(TIMER16, &timer_initpara);
	
	timer_interrupt_flag_clear(TIMER16, TIMER_INT_UP);
	timer_interrupt_enable(TIMER16, TIMER_INT_UP);
		
	nvic_irq_enable(TIMER16_IRQn, 6, 0);

	timer_enable(TIMER16);
	
    if(pTimerCallBack == NULL)
    {
        /* Block until timer expires. */
        while(!dwTimerExp){
//				taskYIELD();
		}
    }
	
    return PH_DRIVER_SUCCESS;
}


phStatus_t phDriver_TimerStop(void)
{
	timer_disable(TIMER16);
    /* Disable timer interrupt */
	nvic_irq_disable(TIMER16_IRQn);
    return PH_DRIVER_SUCCESS;
}

static phStatus_t phDriver_PinConfigInterrupt(uint8_t bPortNum, uint8_t bPinNum, phDriver_Pin_Config_t *pPinConfig)
{
	return PH_DRIVER_ERROR | PH_COMP_DRIVER;
}

static void IO_Setup(IO_T* io, uint8_t port, uint8_t pin){		
	io->port = (void*)portTable[port];
	io->pin = BIT(pin);
}

static void IO_Setup_PinNum(IO_T* io, uint32_t dwPinNumber){
    uint8_t bGpioNum;
    uint8_t bPinNum;
    /* Extract the Pin, Gpio details from dwPinNumber */
    bPinNum = (uint8_t)(dwPinNumber & 0xFF);
    bGpioNum = (uint8_t)((dwPinNumber & 0xFF00)>>8);
	IO_Setup(io, bGpioNum, bPinNum);
}

static void IO_Interrupt_Config(uint32_t dwPinNumber, phDriver_Interrupt_Config_t config){
    uint8_t bGpioNum;
    uint8_t bPinNum;
	uint32_t exitn;
	exti_trig_type_enum trig_type;
	
    /* Extract the Pin, Gpio details from dwPinNumber */
    bPinNum = (uint8_t)(dwPinNumber & 0xFF);
    bGpioNum = (uint8_t)((dwPinNumber & 0xFF00)>>8);
	exitn = BIT(bPinNum);
	
	switch(config){
//	    case PH_DRIVER_INTERRUPT_LEVELZERO = 0x01,   /**< Interrupt when level zero. */
//	    case PH_DRIVER_INTERRUPT_LEVELONE,           /**< Interrupt when level one. */
    case PH_DRIVER_INTERRUPT_RISINGEDGE:  trig_type = EXTI_TRIG_RISING; break;       /**< Interrupt on rising edge. */
    case PH_DRIVER_INTERRUPT_FALLINGEDGE: trig_type = EXTI_TRIG_FALLING; break;       /**< Interrupt on falling edge. */
    case PH_DRIVER_INTERRUPT_EITHEREDGE:  trig_type = EXTI_TRIG_BOTH; break;       /**< Interrupt on either edge. */
	default :
		trig_type = EXTI_TRIG_NONE;
	break;
	}
	
	rcu_periph_clock_enable(RCU_SYSCFG);
    /* connect key EXTI line to key GPIO pin */
    syscfg_exti_line_config(bGpioNum, bPinNum);

    /* configure key EXTI line */
    exti_init((exti_line_enum)exitn, EXTI_INTERRUPT, trig_type);
    exti_interrupt_flag_clear((exti_line_enum)exitn);
}

phStatus_t phDriver_PinConfig(uint32_t dwPinNumber, phDriver_Pin_Func_t ePinFunc, phDriver_Pin_Config_t *pPinConfig)
{
    phStatus_t wStatus = PH_DRIVER_SUCCESS;
	IO_T io;
	
	IO_Setup_PinNum(&io, dwPinNumber);
	
    switch(ePinFunc)
    {
    case PH_DRIVER_PINFUNC_INPUT:{
		uint32_t mode;
		switch(pPinConfig->bPullSelect){
		case PH_DRIVER_PULL_UP:{
			mode = IO_Mode_IPU;
		}
		break;
		case PH_DRIVER_PULL_DOWN:{
			mode = IO_Mode_IPD;
		}
		break;
		default:
			mode = IO_Mode_IN;
		break;
		}
		IO_Init(&io, mode);
	}
    break;

    case PH_DRIVER_PINFUNC_OUTPUT:
		IO_Init(&io, IO_Mode_OUT);
		IO_Set(&io, pPinConfig->bOutputLogic);
        break;
	case PH_DRIVER_PINFUNC_INTERRUPT:{
		uint32_t mode;
		switch(pPinConfig->bPullSelect){
		case PH_DRIVER_PULL_UP:{
			mode = IO_Mode_IPU;
		}
		break;
		case PH_DRIVER_PULL_DOWN:{
			mode = IO_Mode_IPD;
		}
		break;
		default:
			 mode = IO_Mode_IN;
		break;
		}
		IO_Init(&io, mode);
		IO_Interrupt_Config(dwPinNumber, pPinConfig->eInterruptConfig);
	}
	break;
    default:
        wStatus = PH_DRIVER_ERROR | PH_COMP_DRIVER;
    }

    return wStatus;

}
		
uint8_t phDriver_PinRead(uint32_t dwPinNumber, phDriver_Pin_Func_t ePinFunc)
{
    uint8_t bValue;

    if(ePinFunc == PH_DRIVER_PINFUNC_INTERRUPT)
    {
		uint8_t bGpioNum;
		uint8_t bPinNum;
		/* Extract the Pin, Gpio details from dwPinNumber */
		bPinNum = (uint8_t)(dwPinNumber & 0xFF);
		bGpioNum = (uint8_t)((dwPinNumber & 0xFF00)>>8);
		uint32_t exitn = BIT(bPinNum);
		if(RESET != exti_interrupt_flag_get(exitn)){
			bValue = 1;
		}
		else{
			bValue = 0;
		}
    }
    else
    {
		IO_T io;
		IO_Setup_PinNum(&io, dwPinNumber);
		bValue = IO_Get(&io);
    }

    return bValue;
}

phStatus_t phDriver_IRQPinPoll(uint32_t dwPinNumber, phDriver_Pin_Func_t ePinFunc, phDriver_Interrupt_Config_t eInterruptType)
{
    uint8_t    bGpioState = 0;

    if ((eInterruptType != PH_DRIVER_INTERRUPT_RISINGEDGE) && (eInterruptType != PH_DRIVER_INTERRUPT_FALLINGEDGE))
    {
        return PH_DRIVER_ERROR | PH_COMP_DRIVER;
    }

    if (eInterruptType == PH_DRIVER_INTERRUPT_FALLINGEDGE)
    {
        bGpioState = 1;
    }

	while(phDriver_PinRead(dwPinNumber, ePinFunc) == bGpioState){

	}

    return PH_DRIVER_SUCCESS;
}

void phDriver_PinWrite(uint32_t dwPinNumber, uint8_t bValue)
{
	IO_T io;
	
	IO_Setup_PinNum(&io, dwPinNumber);
	IO_Set(&io, bValue);
}


void phDriver_PinClearIntStatus(uint32_t dwPinNumber)
{
    uint8_t bGpioNum;
    uint8_t bPinNum;

    /* Extract the Pin, Gpio details from dwPinNumber */
    bPinNum = (uint8_t)(dwPinNumber & 0xFF);
    bGpioNum = (uint8_t)((dwPinNumber & 0xFF00)>>8);
	uint32_t exitn = BIT(bPinNum);
	exti_interrupt_flag_clear(exitn);
}

phStatus_t phDriver_IRQPinRead(uint32_t dwPinNumber)
{
	phStatus_t bGpioVal = false;

	bGpioVal = phDriver_PinRead(dwPinNumber, PH_DRIVER_PINFUNC_INPUT);

	return bGpioVal;
}


void phDriver_PinReset_Set(uint8_t bValue){
	RFIO_SetReset(bValue);
}

void phDriver_PinNSS_Set(uint8_t bValue){
	RFIO_SPI_SetNSS(bValue);
}


void TIMER16_IRQHandler(void)
{
	timer_interrupt_flag_clear(TIMER16, TIMER_INT_UP);
	phDriver_TimerStop();
    pTimerIsrCallBack();
}

static void phDriver_TimerIsrCallBack(void)
{
    dwTimerExp = 1;
}

void phDriver_EnterCriticalSection(void)
{
//	    __disable_irq();
	vPortEnterCritical();
}

void phDriver_ExitCriticalSection(void)
{
//	    __enable_irq();
	vPortExitCritical();
}


